package cn.bonoon.kernel.filters;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import cn.bonoon.kernel.VisibleScope;
import cn.bonoon.kernel.events.BaseEvent;
import cn.bonoon.kernel.support.entities.EntityScopable;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.support.searcher.StandardFinder;

/**
 * 操作范围的检查
 * @author jackson
 *
 */
public interface VisibleScopeFilter {
	/**
	 * <pre>
	 * 用于检查数据的范围对于当前操作的用户是否可见
	 * 
	 * 本方法在检查不通过的情况下会抛出异常
	 * </pre>
	 * @param event
	 * @param entity
	 */
	void check(BaseEvent event, Persistable entity);
	
	void check(BaseEvent event, EntityManager entityManager, Class<?> entityClass, Long id);

	/**
	 * <pre>
	 * 对查询语句进行范围限制
	 * </pre>
	 * @param event
	 * @param finder
	 */
	void filter(BaseEvent event, StandardFinder finder);
	
	Query filter(BaseEvent event, EntityManager entityManager, StringBuilder select, String orderby);
	
	VisibleScopeFilter EMPTY = new VisibleScopeFilter(){

		@Override
		public void check(BaseEvent event, Persistable entity) { }
		
		@Override
		public void check(BaseEvent event, EntityManager entityManager, Class<?> entityClass, Long id) { }
		
		@Override
		public void filter(BaseEvent event, StandardFinder finder) { }
		
		@Override
		public Query filter(BaseEvent event, EntityManager entityManager, StringBuilder select, String orderby) {
			return entityManager.createQuery(select.append(orderby).toString());
		}
	};

	/**
	 * <pre>
	 * 关于用户操作权限的限制：
	 * 1.一般的用户只允许操作自己所属域的数据
	 * 2.超级管理的操作权限没有限制
	 * 3.可以通过操作范围的指定来限定，这样一来，普通的用户也可以设置为操作全部的数据
	 * </pre>
	 * @author jackson
	 *
	 */
	VisibleScopeFilter STANDRAD = new VisibleScopeFilter() {

		@Override
		public void check(BaseEvent event, Persistable entity) {
			EntityScopable scope = (EntityScopable)entity;
			VisibleScope es = event.getScope();
			// 首先，如果想操作不同的域，必须的前提条件就是超级管理员并且被设置为NULL，否则不允许操作
			if(es == VisibleScope.GLOBAL || (event.isSuper() && es == VisibleScope.NULL)){
				//超级管理员，并且没有定义专属域的情况
				return;
			}
			
			Long soid = scope.getOwnerId(), toid = event.getOwnerId();
			if((soid == null && toid == null) || (soid != null && soid.equals(toid))){
				//相同域的操作处理
				if(es == VisibleScope.DOMAIN || (event.isAdmin() && es == VisibleScope.NULL)){
					return;
				}
				Long sid = scope.getCreatorId(), tid = event.getId();
				if(es == VisibleScope.EXCLUSIVE && tid.equals(sid)){
					//专属域，只允许编辑自己的数据
					return;
				}
			}
			
			throw new RuntimeException("您不允许查看/操作该数据！");
		}
		
		@Override
		public void check(BaseEvent event, EntityManager entityManager, Class<?> entityClass, Long id) {
			VisibleScope scope = event.getScope();
			if(scope == VisibleScope.DOMAIN){
				StringBuilder select = new StringBuilder("select count(x) from ");
				select.append(entityClass.getName()).append(" x where x.id=? and x.ownerId=?");
				TypedQuery<Number> query = entityManager.createQuery(select.toString(), Number.class);
				query.setParameter(1, id).setParameter(2, event.toOwnerId());
				if(query.getSingleResult().intValue() == 0){
					throw new RuntimeException("找不到对象！");
				}
			}else if(scope == VisibleScope.EXCLUSIVE || !(event.isSuper() || scope == VisibleScope.GLOBAL)){
				StringBuilder select = new StringBuilder("select count(x) from ");
				select.append(entityClass.getName()).append(" x where x.id=? and x.ownerId=? and x.creatorId=?");
				TypedQuery<Number> query = entityManager.createQuery(select.toString(), Number.class);
				query.setParameter(1, id).setParameter(2, event.toOwnerId()).setParameter(3, event.getId());
				if(query.getSingleResult().intValue() == 0){
					throw new RuntimeException("找不到对象！");
				}
			}
		}

		@Override
		public void filter(BaseEvent event, StandardFinder finder) {
			VisibleScope scope = event.getScope();
			if(scope == VisibleScope.DOMAIN){
				finder.and("x.ownerId=?", event.toOwnerId());
			}else if(scope == VisibleScope.EXCLUSIVE || !(event.isSuper() || scope == VisibleScope.GLOBAL)){
				finder.and("x.ownerId=? and x.creatorId=?", event.toOwnerId(), event.getId());
			}
		}

		@Override
		public Query filter(BaseEvent event, EntityManager entityManager, StringBuilder select, String orderby) {
			VisibleScope scope = event.getScope();
			if(scope == VisibleScope.DOMAIN){
				select.append(" and x.ownerId=?").append(orderby);
				Query query = entityManager.createQuery(select.toString());
				return query.setParameter(1, event.toOwnerId());
			}else if(scope == VisibleScope.EXCLUSIVE || !(event.isSuper() || scope == VisibleScope.GLOBAL)){
				select.append(" and x.ownerId=? and x.creatorId=?").append(orderby);
				Query query = entityManager.createQuery(select.toString());
				return query.setParameter(1, event.toOwnerId()).setParameter(2, event.getId());
			}
			return entityManager.createQuery(select.append(orderby).toString());
		}

	};
}
